/*
 * Exploder
 * 
 * Tero Pyylampi (218437), Jussi Tuurinkoski (211594)
 *
 * Abstract base class for all the physic objects in the game
 */

#include "GamePhysicObject.h"

// default constructor
GamePhysicObject::GamePhysicObject( b2World *world )
	: GameObject(), mGameWorld( world ) {
	mBodies = new std::vector< b2Body* >();
}

// default destructor
GamePhysicObject::~GamePhysicObject() {
	for( unsigned int i = 0; i < mBodies->size(); ++i )
		mGameWorld->DestroyBody( mBodies->at( i ) );
	delete mBodies;
}

// default update function
void GamePhysicObject::update( cocos2d::ccTime dt ) {
	GameObject::update( dt );
}

// object takes damage as a result of a collisiong
void GamePhysicObject::takeDamage( float damage, b2Body *bodyTakingDamage ) {
	// TODO: should be overwritten by a construction elements?
}

// set new color for object sprites
void GamePhysicObject::setObjectColor( cocos2d::ccColor3B c ) {
	// TODO: ?
}

// get the physics objects
std::vector< b2Body* > *GamePhysicObject::getBodyObjects() {
	return mBodies;
}

// get the main type of the object
PhysicObjectItems GamePhysicObject::getObjectType() {
	return ELEMENT_INVALID;
}

// calculate length between my position and given point
float GamePhysicObject::calculateDistanceTo( b2Vec2 worldPoint ) {
	if( mBodies->size() > 0 ) {
		b2Vec2 myPos = mBodies->at( 0 )->GetPosition();
		return std::sqrt( std::pow( std::abs( worldPoint.x - myPos.x ), 2 ) + std::pow( std::abs( worldPoint.y - myPos.y ), 2 ) );
	}

	return 99999.0f;
}

// check whether this object is asleep
bool GamePhysicObject::currentlyAsleep() {
	for( unsigned int i = 0; i < mBodies->size(); ++i )
		if( mBodies->at( i )->GetLinearVelocity().Length() > OBJECT_MOVING_TRESHOLD /*mBodies->at( i )->IsAwake()*/ )
			return false;
	return true;
}

// check whether this object owns this specific body
bool GamePhysicObject::ownerOfBody( b2Body *body ) {
	for( unsigned int i = 0; i < mBodies->size(); ++i )
		if( mBodies->at( i ) == body )
			return true;
	return false;
}

// get absolute screen position of the sprite
cocos2d::CCPoint GamePhysicObject::getObjectScreenPosition() {
	return ccp( 0, 0 );
}

// create a single triangle with given width & height
b2Body *GamePhysicObject::createTriangle( bool kinematic, float width, float height, float cornerX, float cornerY, ObjectProperties &properties, TriangleData data ) {
	// create physics body
	b2BodyDef bodyDef;
	bodyDef.type = kinematic ? b2_kinematicBody : b2_dynamicBody;
	bodyDef.position.Set( cornerX, cornerY );
	b2Body *body = mGameWorld->CreateBody( &bodyDef );
	body->SetUserData( this );

	// set shape for body
	b2PolygonShape triangleShape;
	b2Vec2 vertices[ 3 ];
	switch( data ) {
	case BOTTOM_LEFT:
		vertices[ 0 ].Set( -width * 0.5f, -height * 0.5f );
		vertices[ 1 ].Set( width * 0.5f, -height * 0.5f );
		vertices[ 2 ].Set( -width * 0.5f, height * 0.5f );
		break;
	case BOTTOM_RIGHT:
		vertices[ 0 ].Set( width * 0.5f, -height * 0.5f );
		vertices[ 1 ].Set( width * 0.5f, height * 0.5f );
		vertices[ 2 ].Set( -width * 0.5f, -height * 0.5f );
		break;
	case TOP_LEFT:
		vertices[ 0 ].Set( -width * 0.5f, height * 0.5f );
		vertices[ 1 ].Set( width * 0.5f, height * 0.5f );
		vertices[ 2 ].Set( -width * 0.5f, -height * 0.5f );
		break;
	case TOP_RIGHT:
		vertices[ 0 ].Set( width * 0.5f, height * 0.5f );
		vertices[ 1 ].Set( -width * 0.5f, height * 0.5f );
		vertices[ 2 ].Set( width * 0.5f, -height * 0.5f );
		break;
	}
	triangleShape.Set( vertices, 3 );

	// create fixture for body
	b2FixtureDef bodyFix;
	bodyFix.shape = &triangleShape;
	bodyFix.density = properties.density;
	bodyFix.friction = properties.friction;
	bodyFix.restitution = properties.restitution;

	// include fixture to the body and create a new body object
	body->CreateFixture( &bodyFix );
	return body;
}

// create a single circle shaped object
b2Body *GamePhysicObject::createCircle( bool kinematic, float radius, float centerX, float centerY, ObjectProperties &properties ) {
	// create physics body
	b2BodyDef bodyDef;
	bodyDef.type = kinematic ? b2_kinematicBody : b2_dynamicBody;
	bodyDef.position.Set( centerX, centerY );
	b2Body *body = mGameWorld->CreateBody( &bodyDef );
	body->SetUserData( this );

	// set shape for body
	b2CircleShape circleShape;
	circleShape.m_radius = radius;

	// create fixture for body
	b2FixtureDef bodyFix;
	bodyFix.shape = &circleShape;
	bodyFix.density = properties.density;
	bodyFix.friction = properties.friction;
	bodyFix.restitution = properties.restitution;

	// include fixture to the body and create a new body object
	body->CreateFixture( &bodyFix );
	return body;
}

// create a single box shaped object with given width and height
b2Body *GamePhysicObject::createBox( bool kinematic, float width, float height, float centerX, float centerY, ObjectProperties &properties ) {
	// create physics body
	b2BodyDef bodyDef;
	bodyDef.type = kinematic ? b2_kinematicBody : b2_dynamicBody;
	bodyDef.position.Set( centerX, centerY );
	b2Body *body = mGameWorld->CreateBody( &bodyDef );
	body->SetUserData( this );

	// set shape for body
	b2PolygonShape boxShape;
	boxShape.SetAsBox( width / 2.0f, height / 2.0f );

	// create fixture for body
	b2FixtureDef bodyFix;
	bodyFix.shape = &boxShape;
	bodyFix.density = properties.density;
	bodyFix.friction = properties.friction;
	bodyFix.restitution = properties.restitution;

	// include fixture to the body and create a new body object
	body->CreateFixture( &bodyFix );
	return body;
}
